﻿using Blm.PgsqlDriver.Helpers;
using Blm.Utils.Extensions;
using Language.Resource;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Core.Model.Ms;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.DtoModel.Sys;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.Repository.Interface.Sys;
using VisionCloud.Service.Ms;
using VisionCloud.Utility;
using VisionCloud.Utility.Redis;

namespace VisionCloud.Service.Sys
{
    public class MsProjectBaseData
    {
        public List<MsProjectStandard> MsProjectStandardList { get; set; }
        public List<MsProjectStandardMeasureItem> MsProjectStandardMeasureItemList { get; set; }
        public List<MsProjectStandardMeasureItemValue> MsProjectStandardMeasureItemValueList { get; set; }
        public List<MsPointType> MsPointTypeList { get; set; }
        public List<MsPointTypeMeasureItem> MsPointTypeMeasureItemList { get; set; }

        /// <summary>
        /// 需要同步的项目
        /// </summary>
        public List<MsProject> MsProjectList { get; set; }

        public List<MsHouseMap> MsHouseMapList { get; set; }
        public List<MsHouseMapPoint> MsHouseMapPointList { get; set; }
        public List<MsMeasureTemplate> MsMeasureTemplateList { get; set; }
        public List<MsMeasureItem> MsMeasureItemList { get; set; }

        public Dictionary<Guid, byte[]> HouseMapImages { get; set; } = new Dictionary<Guid, byte[]>();

        public List<Guid> AllProjectListId { get; set; } = new List<Guid>();

        /// <summary>
        /// Surface端需要删除的项目IDs
        /// </summary>
        public List<Guid> YunNeedDelProJIds { get; set; }

        public List<MsProjectAlgorithm> MsProjectAlgorithmList { get; set; }

        public List<MsAlgorithmParamTemplate> MsAlgorithmParamTemplateList { get; set; }

        public List<MsAlgorithmParamTemplateItem> MsAlgorithmParamTemplateItemList { get; set; }

        public List<MsHouseMapDetails> MsHouseMapDetailsList { get; set; }

        /// <summary>
        /// 对齐分割Excel
        /// </summary>
        public Dictionary<Guid, byte[]> ExcelBytes { get; set; } = new Dictionary<Guid, byte[]>();

        public Dictionary<Guid, byte[]> InnerWallBytes { set; get; } = new Dictionary<Guid, byte[]>();
        public Dictionary<Guid, byte[]> InnerWallLineBytes { set; get; } = new Dictionary<Guid, byte[]>();
    }

    public class DataSynchronizationService
    {
        public IMsProjectStandardRepository IMsProjectStandardRepository { get; set; }
        public IMsProjectStandardMeasureItemRepository IMsProjectStandardMeasureItemRepository { get; set; }
        public IMsProjectStandardMeasureItemValueRepository IMsProjectStandardMeasureItemValueRepository { get; set; }
        public IMsPointTypeRepository IMsPointTypeRepository { get; set; }
        public IMsPointTypeMeasureItemRepository IMsPointTypeMeasureItemRepository { get; set; }
        public IMsProjectRepository IMsProjectRepository { get; set; }
        public IMsHouseMapRepository IMsHouseMapRepository { get; set; }
        public IMsHouseMapPointRepository IMsHouseMapPointRepository { get; set; }
        public IMsMeasureTemplateRepository IMsMeasureTemplateRepository { get; set; }
        public IMsMeasureItemRepository IMsMeasureItemRepository { get; set; }
        public IMsHouseMapDetailsRepository IMsHouseMapDetailsRepository { get; set; }
        public IStringLocalizer<ServiceResource> Localizer { get; set; }
        public StatisService statisService { get; set; }
        public IAlgorithmRepository AlgorithmRepository { get; set; }

        private const string FileUploadHashName = "FileUploadHash";
        private const string FileUploadHashLockName = "FileUploadHashLockName";
        private readonly IConfiguration config;
        private string MyAppInstanceName = "MyAppInstanceName";
        private int FileUploadTimeOutMinutes = 1;
        private int MaxFileUploadLenght = 30;
        public TimeSpan ClearUploadQueueTime { get; set; } = TimeSpan.FromMinutes(30);

        public ISysUserRepository SysUserRepository { get; set; }

        /// <summary>
        /// 允许使用最新标准库公司名单
        /// </summary>
        private static string AllowCompanyIds = ConfigHelper.GetValue<string>("AllowCompanyIds");

        public DataSynchronizationService(IConfiguration config)
        {
            try
            {
                FileUploadTimeOutMinutes = config.GetSection("FileUploadTimeOutMinutes").Get<int>();
                MaxFileUploadLenght = config.GetSection("MaxFileUploadLenght").Get<int>();
                ClearUploadQueueTime = TimeSpan.FromMinutes(config.GetSection("ClearUploadQueueTime").Get<int>());
                MyAppInstanceName = config.GetSection("MyAppInstanceName").Get<string>();
            }
            catch (Exception ex)
            {
                FileUploadTimeOutMinutes = 40;
                MaxFileUploadLenght = 30;
                ClearUploadQueueTime = TimeSpan.FromMinutes(20);
            }

            this.config = config;
        }

        /// <summary>
        /// 获取当前用户关联的项目基础信息
        /// </summary>
        /// <param name="userGuid"></param>
        /// <param name="softwareVersion"></param>
        /// <param name="projectIdAndVersionDto"></param>
        /// <returns></returns>
        public async Task<MsProjectBaseData> GetProjectBaseDate(Guid userGuid, string softwareVersion, ProjectIdAndVersionDto[] projectIdAndVersionDto)
        {
            var condition = new UserProjectStatisDto();
            condition.YunUserGuid = userGuid;
            List<ProjectInfoDto> newPIList = new List<ProjectInfoDto>();
            List<ProjectInfoDto> projectInfo = null;
            List<ProjectInfoDto> allPojectInfo = null;
            var mpb = new MsProjectBaseData();

            //需要删除的项目id
            List<Guid> neebDelProjIds = null;

            //从缓存里面获取，较少数据库查询
            var key = RedisKeyConstant.ProjectVersionKey(userGuid);
            allPojectInfo = await RedisHelper.Instance.GetAsync<List<ProjectInfoDto>>(key);

            if (allPojectInfo == null)
            {
                //查询云端该用户关联的未删除的项目
                allPojectInfo = await statisService.GetUserProjectInfo(condition, false);

                //Surface端未删除的项目id
                var projList = projectIdAndVersionDto.Where(b => !b.IsDeleted).Select(a => a.ProjectId).ToList();

                //查询云端已删除但Surface端未删除的项目
                var needDelProjs = await IMsProjectRepository.WhereAsync(x => projList.Contains(x.ProjectId) && x.IsDeleted);
                if (needDelProjs?.Count > 0)
                {
                    needDelProjs.ForEach(x => allPojectInfo.Add(new ProjectInfoDto { ProjectId = x.ProjectId, IsDelete = true }));
                }

                //缓存用户关联的所有项目（包括已删除的）
                await RedisHelper.Instance.SetAsync(key, allPojectInfo, new TimeSpan(1, 0, 0));
            }

            //注意：缓存里已包括本地端还没删除但是云端已经删除的项目id
            neebDelProjIds = allPojectInfo?.Where(i => i.IsDelete)?.Select(x => x.ProjectId)?.ToList();
            allPojectInfo = allPojectInfo?.Where(i => i.IsDelete == false)?.ToList();
            if (allPojectInfo.Count == 0)
            {
                mpb.AllProjectListId = new List<Guid>();
            }
            projectInfo = allPojectInfo.Where(i => i.IsCanPull).ToList();

            //过滤需要更新的项目
            foreach (var item in projectInfo)
            {
                //客户端没有这项目则需要增加到更新项目列表
                if (!projectIdAndVersionDto.Any(i => i.ProjectId == item.ProjectId))
                {
                    newPIList.Add(item);
                }
                else
                {
                    //项目版本较低的也需要更新
                    if (projectIdAndVersionDto.Any(i => (i.ProjectId == item.ProjectId) && (i.ProjectVersion < item.ProjectVersion)))
                    {
                        newPIList.Add(item);
                    }
                }
            }
            if (newPIList.Count == 0)
            {
                mpb.AllProjectListId = allPojectInfo.Select(i => i.ProjectId).ToList();
            }
            else
            {
                mpb = await GetProjectBaseInfo(newPIList.Select(i => i.ProjectId).ToArray());
                mpb.AllProjectListId = allPojectInfo.Select(i => i.ProjectId).ToList();
            }

            mpb.YunNeedDelProJIds = neebDelProjIds;
            return mpb;
        }

        /// <summary>
        /// 获取执行项目的基础信息
        /// </summary>
        /// <param name="ProjectIds"></param>
        /// <returns></returns>
        private async Task<MsProjectBaseData> GetProjectBaseInfo(Guid[] ProjectIds)
        {
            #region 过滤不能使用装饰阶段的公司

            Guid excludeStandard = "a92e1059-c1dd-4a1f-8470-514095381c46".ToGuid();
            Guid currentUserCompanyId = OperatorProvider.Get().CompanyGuid;
            if (OperatorProvider.Get() != null && currentUserCompanyId == Guid.Empty)
            {
                var sysUser = await SysUserRepository.GetByIdAsync(OperatorProvider.Get().Id);
                currentUserCompanyId = sysUser.SysOrgId.ToGuid();
            }
            if (!string.IsNullOrWhiteSpace(AllowCompanyIds)
                && AllowCompanyIds.Contains(currentUserCompanyId.ToString()))
            {
                excludeStandard = Guid.Empty;
            }
            if (string.Equals(AllowCompanyIds, "*"))
            {
                excludeStandard = Guid.Empty;
            }

            #endregion 过滤不能使用装饰阶段的公司

            MsProjectBaseData mpdd = new MsProjectBaseData
            {
                // 同步标准库、测量项和标注值
                MsProjectStandardList = await IMsProjectStandardRepository.GetDataByProjectsWithStandardId(ProjectIds, excludeStandard),
                MsProjectStandardMeasureItemList = await IMsProjectStandardMeasureItemRepository.GetDataByProjectsWithStandardId(ProjectIds, excludeStandard),
                MsProjectStandardMeasureItemValueList = await IMsProjectStandardMeasureItemValueRepository.GetDataByProjectsWithStandardId(ProjectIds, excludeStandard),

                // 同步站点类型和站点测量项
                MsPointTypeList = await IMsPointTypeRepository.GetDataByProjects(ProjectIds),
                MsPointTypeMeasureItemList = await IMsPointTypeMeasureItemRepository.GetDataByProjectsWithStandardId(ProjectIds, excludeStandard),
                MsProjectList = await IMsProjectRepository.GetDataByProjects(ProjectIds),

                MsHouseMapList = await IMsHouseMapRepository.GetDataByProjects(ProjectIds),

                MsProjectAlgorithmList = await AlgorithmRepository.GetMsProjectAlgorithmsByProjIds(ProjectIds),
                MsAlgorithmParamTemplateItemList = await AlgorithmRepository.GetAlgorithmParamTemplateItemsByProjIds(ProjectIds),
                MsAlgorithmParamTemplateList = await AlgorithmRepository.GetMsAlgorithmParamTemplatesByProjIds(ProjectIds),
                MsHouseMapDetailsList = await IMsHouseMapDetailsRepository.WhereAsync(i => ProjectIds.Contains(i.ProjectId))
            };
            var houseMapIds = mpdd.MsHouseMapList.Select(id => id.HouseMapId).ToArray();
            mpdd.MsHouseMapPointList = await IMsHouseMapPointRepository.GetDataByHouseMapIds(houseMapIds);
            mpdd.MsMeasureTemplateList = await IMsMeasureTemplateRepository.GetDataByhouseMapIds(houseMapIds);
            mpdd.MsMeasureItemList = await IMsMeasureItemRepository.GetDataByhouseMapIds(houseMapIds);

            bool isMeasurer = OperatorProvider.Get().RoleNameList.Contains("测量员");
            if (mpdd.MsHouseMapList != null && mpdd.MsHouseMapList.Count > 0)
            {
                foreach (var item in mpdd.MsHouseMapList)
                {
                    MsProject project = mpdd.MsProjectList.Where(x => x.ProjectId == item.ProjectId).FirstOrDefault();
                    var isCanPublice = project != null && project.PubliceDate.HasValue && project.PubliceDate.Value >= item.CreatedDate;
                    if (!isCanPublice)
                        continue;

                    var path = GetHouseMapImagePath((item.DataType ?? 0) == 0 ? 0 : 1, item.Image_Base);
                    if (File.Exists(path))
                    {
                        mpdd.HouseMapImages.Add(item.HouseMapId, await File.ReadAllBytesAsync(path));
                    }
                    if ((item.DataType ?? 0) != 0 && item.Image_Base.IsNotNull())
                    {
                        var extension = Path.GetExtension(path);
                        item.Image_Base = $"{item.HouseMapId}{extension}";
                    }
                    if ((item.DataType ?? 0) != 0 && item.ExcelPath.IsNotNull())
                    {
                        var excelFile = $"{config["AlgorithmReport"]}/{item.ExcelPath.Replace("algorithm-report", "")}";
                        if (File.Exists(excelFile))
                        {
                            var extension = Path.GetExtension(excelFile);
                            item.ExcelPath = $"{item.HouseMapId}{extension}";
                            mpdd.ExcelBytes.Add(item.HouseMapId, await File.ReadAllBytesAsync(excelFile));
                        }
                    }
                    if (!string.IsNullOrEmpty(item.InnerWallLinePath))
                    {
                        var innerWallLinePath = $"{config["AlgorithmReport"]}/{item.InnerWallLinePath.Replace("algorithm-report", "")}";
                        if (File.Exists(innerWallLinePath))
                        {
                            var extension = Path.GetFileName(innerWallLinePath);
                            item.InnerWallLinePath = extension;
                            mpdd.InnerWallLineBytes.Add(item.HouseMapId, await File.ReadAllBytesAsync(innerWallLinePath));
                        }
                    }
                    if (!string.IsNullOrEmpty(item.InnerWallPath))
                    {
                        var innerWallPath = $"{config["AlgorithmReport"]}/{item.InnerWallPath.Replace("algorithm-report", "")}";
                        if (File.Exists(innerWallPath))
                        {
                            var extension = Path.GetFileName(innerWallPath);
                            item.InnerWallPath = extension;
                            mpdd.InnerWallBytes.Add(item.HouseMapId, await File.ReadAllBytesAsync(innerWallPath));
                        }
                    }
                }
            }

            string ids = "";
            mpdd.MsProjectList.ForEach(item =>
            {
                ids = (string.IsNullOrEmpty(ids) ? "" : (ids + ",")) + $"'{item.ProjectId}'";
                if (string.IsNullOrEmpty(item.Details))
                {
                    var detailList = JsonConvert.DeserializeObject<List<DetailInfo>>(item.Details);
                    detailList.RemoveRange(item.PubliceDetailsCount, detailList.Count - item.PubliceDetailsCount);
                    item.Details = JsonConvert.SerializeObject(detailList);
                }
            });
            if (!string.IsNullOrWhiteSpace(ids))
            {
                var updateSql = $@"update ""MsProject"" set ""IsPullByClient"" = true where ""ProjectId"" in ({ids});";
                await PgSqlHelper.ExecuteNonQueryAsync(System.Data.CommandType.Text, updateSql);
            }

            return mpdd;
        }

        /// <summary>
        /// 获取户型图存储的绝对路径
        /// </summary>
        /// <param name="dateType">0实测任务户型图，1机器人作业户型图</param>
        /// <param name="image_Base"></param>
        /// <returns></returns>
        /// <exception cref="OperationException"></exception>
        public string GetHouseMapImagePath(int dateType, string image_Base) => dateType switch
        {
            0 => FileInfoUtils.GetAbsolutePathFromRelative(config["FileUploadFileMaster"] + "/" + config["FileUploadFileSon"] + "/" + image_Base),
            1 => $"{config["AlgorithmReport"]}/{image_Base.Replace("algorithm-report", "")}",
            _ => throw new OperationException("暂不支持该类型的户型图")
        };

        /// <summary>
        /// 获取项目的户型图
        /// </summary>
        /// <param name="projectIds"></param>
        /// <returns></returns>
        public async ValueTask<ApiResult> GetProjectHouseMaps(Guid[] projectIds)
        {
            if (projectIds == null || projectIds.Count() == 0)
                return ApiResult.Success();

            var list = await IMsHouseMapDetailsRepository.WhereAsync(i => projectIds.Contains(i.ProjectId));
            return ApiResult.Success().SetData(list);
        }

        /// <summary>
        ///新增上传文件
        /// </summary>
        /// <param name="dogNumber"></param>
        /// <param name="UploadType"></param>
        /// <returns></returns>
        public async Task<ResultAndMessage> AddInFileUploadQueue(string dogNumber, string UploadType)
        {
            ResultAndMessage rm = new ResultAndMessage();
            var lenght = await RedisHelper.Instance.HashLengthAsync(FileUploadHashName);
            if (lenght > MaxFileUploadLenght)
            {
                rm.Message = Localizer["YB0218"].Value;
                rm.IsSuccess = false;
                return rm;
            }
            else
            {
                var hk = new StackExchange.Redis.RedisValue(dogNumber);
                var isExist = await RedisHelper.Instance.HashExistsAsync(FileUploadHashName, hk);
                if (!isExist)
                {
                    var hv = new StackExchange.Redis.RedisValue(DateTime.Now.ToDateTimeStandardString());
                    var HashEntry = new StackExchange.Redis.HashEntry(hk, hv);
                    await RedisHelper.Instance.HashSetAsync(FileUploadHashName, new StackExchange.Redis.HashEntry[] { HashEntry });
                    rm.Message = string.Empty;
                    rm.IsSuccess = true;
                    return rm;
                }
                else
                {
                    rm.Message = Localizer["YB0219"].Value;
                    rm.IsSuccess = true;
                    return rm;
                }
            }
        }

        /// <summary>
        /// 移除上传文件
        /// </summary>
        /// <param name="dogNumber"></param>
        /// <returns></returns>
        public async Task<ResultAndMessage> OutFileUploadQueue(string dogNumber)
        {
            ResultAndMessage rm = new ResultAndMessage();
            var hk = new StackExchange.Redis.RedisValue(dogNumber);
            rm.IsSuccess = await RedisHelper.Instance.HashDeleteAsync(FileUploadHashName, hk);
            return rm;
        }

        /// <summary>
        /// 清理超时的上传文件
        /// </summary>
        /// <returns></returns>
        public async Task<bool> ClearTimeOutFileUploadQueue()
        {
            try
            {
                if (await RedisHelper.Instance.LockTakeAsync(FileUploadHashLockName, MyAppInstanceName, TimeSpan.FromSeconds(10)))
                {
                    //如果获取锁成功，执行下面清除操作
                    try
                    {
                        //获取所有行
                        var kvList = await RedisHelper.Instance.HashGetAllAsync(FileUploadHashName);
                        if (kvList.Length > 0)
                        {
                            //循环清除超时，或者格式不符合要求的行
                            foreach (var kvItem in kvList)
                            {
                                if (DateTime.TryParse(kvItem.Value, out DateTime dt))
                                {
                                    if (DateTime.Now.Subtract(dt).TotalMinutes > FileUploadTimeOutMinutes)
                                    {
                                        await RedisHelper.Instance.HashDeleteAsync(FileUploadHashName, kvItem.Name);
                                    }
                                }
                                else
                                {
                                    await RedisHelper.Instance.HashDeleteAsync(FileUploadHashName, kvItem.Name);
                                }
                            }
                        }
                        //处理完成返回true
                        return true;
                    }
                    finally
                    {
                        //释放锁
                        await RedisHelper.Instance.LockReleaseAsync(FileUploadHashLockName, MyAppInstanceName);
                    }
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ClearTimeOutFileUploadQueue 方法出错", ex);
                return false;
            }
        }
    }
}